﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AddIn.EA.Architecture.ComponentLogic.ElementModel;
using System.Runtime.Serialization;

namespace AddIn.EA.Architecture.ComponentLogic.StandardProject.Builders
{
    [Serializable()]
    public abstract class ElementBuilder : ISerializable
    {
        public abstract void BuildStructure(ComponentLogic.ElementModel.Package viewModel, AddIn.EA.Framework.RepositoryManager templateRepository, BusinessEntityStandardProject itemEntity);
        public abstract void Validate();

        /// <summary>
        /// Check the requested Package on the source structure provided.
        /// If there is the package, get and return the instance, otherwise create a new one and return as well.
        /// </summary>
        /// <param name="packageName">The Package to search for</param>
        /// <param name="sourcePackage">The Source Package to be seek.</param>
        /// <param name="recursive">Indicate if the search will consider child packages on the source package.</param>
        /// <returns>The package according the requested packageName</returns>
        protected ElementModel.Package GetViewModelPackage(String packageName, ElementModel.Package sourcePackage, bool recursive)
        {
            ElementModel.Package resultPackage = null;
            if (sourcePackage != null)
            {
                resultPackage = sourcePackage.FindPackage(packageName, recursive);
                if (resultPackage == null)
                {
                    resultPackage = new AddIn.EA.Architecture.ComponentLogic.ElementModel.Package(packageName);
                    sourcePackage.Packages.Add(resultPackage);
                }
            }
            return resultPackage;
        }
        /// <summary>
        /// Create a new ElementModel Attribute based on the Logic model created for the entity.
        /// </summary>
        /// <param name="itemProperty">The property from the logic model.</param>
        /// <returns>The result Attribute in the EA format.</returns>
        protected ElementModel.Attribute CreateElementModelAttribute(ComponentLogic.StandardProject.PropertyStandardProject itemProperty)
        {
            return new ElementModel.Attribute()
            {
                InitialValue = itemProperty.InitialValue,
                IsConst = itemProperty.IsConstant,
                IsStatic = itemProperty.IsStatic,
                Name = itemProperty.Name,
                Notes = itemProperty.Notes,
                Scope = (Scope)Enum.Parse(typeof(Scope), itemProperty.Scope.ToString()),
                Stereotype = itemProperty.Stereotype,
                Type = itemProperty.Type,
                Encapsulate = itemProperty.Encapsulate
            };
        }
        /// <summary>
        /// Create a new ElementModel Method based on the Logical model created for the entity.
        /// </summary>
        /// <param name="itemMethod"></param>
        /// <returns></returns>
        protected virtual ElementModel.Method CreateElementModelMethod(ComponentLogic.StandardProject.MethodStandardProject itemMethod, String namespaceParameters, String namespaceReturn)
        {
            return new Method
            {
                IsAbstract = itemMethod.IsAbstract,
                IsStatic = itemMethod.IsStatic,
                Name = itemMethod.Name,
                Notes = itemMethod.Notes,
                Parameters = !String.IsNullOrEmpty(namespaceParameters) ? itemMethod.Parameters.Replace("namespace", namespaceParameters) : itemMethod.Parameters,
                Return = !String.IsNullOrEmpty(namespaceReturn) ? itemMethod.Return.Replace("namespace", namespaceReturn) : itemMethod.Return,
                Scope = (Scope)Enum.Parse(typeof(Scope), itemMethod.Scope.ToString())
            };
        }
        /// <summary>
        /// Include all the properties from the Logical Model into the provided element.
        /// </summary>
        /// <param name="element"></param>
        /// <param name="itemEntity"></param>
        protected void BindElementProperties(Element element, BusinessEntityStandardProject itemEntity)
        {
            if (itemEntity.Properties != null && itemEntity.Properties.Count > 0)
            {
                foreach (ComponentLogic.StandardProject.PropertyStandardProject itemProperty in itemEntity.Properties)
                    element.Attributes.Add(CreateElementModelAttribute(itemProperty));
            }
        }
        /// <summary>
        /// Include all the methods from the Logical Model into the provided element.
        /// </summary>
        /// <param name="element"></param>
        /// <param name="itemEntity"></param>
        protected void BindElementMethods(Element element, BusinessEntityStandardProject itemEntity)
        {
            BindElementMethods(element, itemEntity, String.Empty, String.Empty);
        }
        /// <summary>
        /// Include all the methods from the Logical Model into the provided element.
        /// </summary>
        /// <param name="element"></param>
        /// <param name="itemEntity"></param>
        protected void BindElementMethods(Element element, BusinessEntityStandardProject itemEntity, String namespaceParameters, String namespaceReturn)
        {
            if (itemEntity.Methods != null && itemEntity.Methods.Count > 0)
            {
                List<MethodStandardProject> methods = itemEntity.Methods.FindAll(it => it.Builders.Exists(itb => itb.GetType() == this.GetType()));
                if (methods != null)
                    methods.ForEach(it => element.Methods.Add(CreateElementModelMethod(it, namespaceParameters, namespaceReturn)));
            }
        }
        
        /// <summary>
        /// Bind the Element Properties getting from the Template Framework Element.
        /// </summary>
        /// <param name="itemEntity"></param>
        /// <param name="templateProcessImplementationElement"></param>
        /// <param name="ProcessImplementationElement"></param>
        protected void BindElementPropertiesFromTemplate(AddIn.EA.Framework.Element template, Element element)
        {
            //Add the Methods from the template element.
            if (template.Attributes != null && template.Attributes.Count > 0)
            {
                if (element.Attributes == null)
                    element.Attributes = new List<AddIn.EA.Architecture.ComponentLogic.ElementModel.Attribute>();

                template.Attributes.ForEach(it => element.Attributes.Add(new ElementModel.Attribute
                {
                    InitialValue = it.InitialValue,
                    IsStatic = it.IsStatic,
                    Name = it.Name,
                    Notes = it.Notes,
                    Stereotype = it.Stereotype,
                    Type = it.Type,
                    Scope = (Scope)Enum.Parse(typeof(Scope), it.Scope.ToString())
                }));
            }
        }
        /// <summary>
        /// Bind the Element Methods getting from the Template Framework Element.
        /// </summary>
        /// <param name="itemEntity"></param>
        /// <param name="templateProcessImplementationElement"></param>
        /// <param name="ProcessImplementationElement"></param>
        protected void BindElementMethodsFromTemplate(AddIn.EA.Framework.Element template, Element element)
        {
            //Add the Methods from the template element.
            if (template.Methods != null && template.Methods.Count > 0)
            {
                if (element.Methods == null)
                    element.Methods = new List<Method>();

                template.Methods.ForEach(it => element.Methods.Add(new Method
                {
                    IsStatic = it.IsStatic,
                    Name = it.Name,
                    Notes = it.Notes,
                    Parameters = GetMethodParametersDescription(it.Parameters),
                    Return = it.ReturnType,
                    Scope = (Scope)Enum.Parse(typeof(Scope), it.Scope.ToString())
                }));
            }
        }
        
        /// <summary>
        /// Get all the properties and methods defined on the template element.
        /// </summary>
        /// <param name="itemEntity"></param>
        /// <param name="template"></param>
        /// <param name="element"></param>
        protected void CopyTemplateElementObjects(Framework.Element template, Element element)
        {
            //Add the methods from the template
            BindElementMethodsFromTemplate(template, element);
            //Add the properties from the template
            BindElementPropertiesFromTemplate(template, element);
        }
        /// <summary>
        /// Build a ElementModel element based on a template element.
        /// This method also include all the properties and methods defined on the template element.
        /// </summary>
        /// <param name="itemEntity"></param>
        /// <param name="templateProcessImplementationElement"></param>
        /// <param name="elementName"></param>
        /// <returns></returns>
        protected virtual Element BuildEntityElementFromTemplate(BusinessEntityStandardProject itemEntity, AddIn.EA.Framework.Element template, String elementName)
        {
            return BuildEntityElementFromTemplate(itemEntity, template, elementName, true);
        }
        /// <summary>
        /// Build a ElementModel element based on a template element.
        /// This method also include all the properties and methods defined on the template element.
        /// "includePropertiesAndMethods" is true for default.
        /// </summary>
        /// <param name="itemEntity"></param>
        /// <param name="template"></param>
        /// <param name="elementName"></param>
        /// <param name="includePropertiesAndMethods"></param>
        /// <returns></returns>
        protected virtual Element BuildEntityElementFromTemplate(BusinessEntityStandardProject itemEntity, AddIn.EA.Framework.Element template, String elementName, bool includePropertiesAndMethods)
        {
            Element element = new Element(elementName);
            element.Stereotype = template.Stereotype;
            element.Notes = itemEntity.Notes;
            element.Scope = (Scope)Enum.Parse(typeof(Scope), itemEntity.Scope.ToString());

            //If is requested, add the properties and methods from the template if they exists
            if (includePropertiesAndMethods)
                CopyTemplateElementObjects(template, element);

            return element;
        }

        /// <summary>
        /// Converts the Parameter List from the Framework object to a String description value.
        /// </summary>
        /// <param name="parameterList"></param>
        /// <returns></returns>
        protected string GetMethodParametersDescription(AddIn.EA.Framework.ParameterList parameterList)
        {
            String result = String.Empty;

            foreach (Framework.Parameter item in parameterList)
                result += item.Name + ":" + item.Type + ";";

            if (!String.IsNullOrEmpty(result))
                result = result.Remove(result.Length - 1, 1);

            return result;
        }

        #region ISerializable Members

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}
